#include "tapi32u8.h"
#include "twin.h"
#include "tapp.h"
#include "tlib.h"
#include "tapi32ex.h"
#include <WinDef.h>

TWin::TWin( TWin* parent )
: THashObj()
, _hWnd( NULL )
, _hAccel( NULL )
, _parent( parent )
, _sleepBusy( false )
{
	_rect.left = _rect.top = _rect.right = _rect.bottom = CW_USEDEFAULT;
	_orgRect = _rect;
}

TWin::~TWin()
{
	Destroy();
}

bool TWin::Create( LPCSTR className /* = NULL */, LPCSTR title /* = "" */, DWORD style /* = */,
				  DWORD exStyle /*= 0*/, HMENU hMenu /*= NULL*/ )
{
	Wstr classNameW( className, BY_MBCS );
	Wstr titleW( title, BY_MBCS );
	return CreateV( classNameW, titleW, style, exStyle, hMenu );
}

bool TWin::CreateU8( LPCSTR className /* = NULL */, LPCSTR title /* = "" */, 
					DWORD style /* = ( WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN )*/,
					DWORD exStyle /*= 0*/, HMENU hMenu /*= NULL*/ )
{
	Wstr className_w( className, BY_UTF8 );
	Wstr title_w( title, BY_UTF8 );
	return CreateV( className_w, title_w, style, exStyle, hMenu );
}

bool TWin::CreateV( const void* className /* = NULL */, const void* title /* = "" */, DWORD style /* = */,
				   DWORD exStyle /*= 0*/, HMENU hMenu /*= NULL*/ )
{
	if( className == NULL )
		className = TApp::GetApp()->GetDefaultClassV();

	TApp::GetApp()->AddWin( this );
	
	_hWnd = TAPI32V::CreateWindowExV( exStyle, className, title, style, _rect.left, _rect.top, 
		_rect.right, _rect.left, _parent ? _parent->_hWnd : NULL, hMenu, 
		TApp::GetApp()->GetInstance(), NULL );
	if( _hWnd == NULL )
		return TApp::GetApp()->DelWin( this ), false;
	else
		return true;
}

void TWin::Show( int mode /* = SW_SHOWDEFAULT */ )
{
	::ShowWindow( _hWnd, mode );
	::UpdateWindow( _hWnd );
}

void TWin::Destroy()
{
	if( ::IsWindow( _hWnd ) )
	{
		::DestroyWindow( _hWnd );
		_hWnd = 0;
	}
}

LRESULT TWin::WinProc( UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	BOOL done = FALSE;
	LRESULT result = 0;
	switch( uMsg )
	{
	case WM_CREATE:
		GetWindowRect( &_orgRect );
		done = EvCreate( lParam );
		break;
	case WM_CLOSE:
		done = EvClose();
		break;
	case WM_COMMAND:
		done = EvCommand( HIWORD(wParam), LOWORD(wParam), lParam );
		break;
	case WM_SYSCOMMAND:
		done = EvSysCommand( wParam, MAKEPOINTS(lParam) );
		break;
	case WM_TIMER:
		done = EvTimer( wParam, (TIMERPROC)lParam );
		break;
	case WM_NCDESTROY:
		GetWindowRect( &_rect );
		if( FALSE == EvNcDestroy() )
			DefWindowProc( uMsg, wParam, lParam );
		done = TRUE;
		TApp::GetApp()->DelWin( this );
		_hWnd = 0;
		break;
	case WM_QUERYENDSESSION:
		result = EvQueryEndSession( (BOOL)wParam, (BOOL)lParam );
		done = TRUE;
		break;
	case WM_ENDSESSION:
		done = EvEndSession( (BOOL)wParam, (BOOL)lParam );
		break;
	case WM_QUERYOPEN:
		result = EvQueryOpen();
		done = TRUE;
		break;
	case WM_PAINT:
		done = EvPaint();
		break;
	case WM_NCPAINT:
		done = EvNcPaint( (HRGN)wParam );
		break;
	case WM_SIZE:
		done = EvSize( (UINT)wParam, LOWORD(lParam), HIWORD(lParam) );
		break;
	case WM_SHOWWINDOW:
		done = EvShowWindow( (BOOL)wParam, (int)lParam );
		break;
	case WM_GETMINMAXINFO:
		done = EvGetMinMaxInfo( (MINMAXINFO*)lParam );
		break;
	case WM_SETCURSOR:
		result = done = EvSetCursor( (HWND)wParam, LOWORD(lParam), HIWORD(lParam) );
		break;
	case WM_MOUSEMOVE:
		done = EvMouseMove( (UINT)wParam, MAKEPOINTS(lParam) );
		break;
	case WM_NCHITTEST:
		done = EvNcHitTest( MAKEPOINTS(lParam), &result );
		break;
	case WM_MEASUREITEM:
		result = done = EvMeasureItem( (UINT)wParam, (LPMEASUREITEMSTRUCT)lParam );
		break;
	case WM_DRAWITEM:
		result = done = EvDrawItem( (UINT)wParam, (LPDRAWITEMSTRUCT)lParam );
		break;
	case WM_NOTIFY:
		result = done = EvNotify( (UINT)wParam, (LPNMHDR)lParam );
		break;
	case WM_CONTEXTMENU:
		result = done = EvContextMenu( (HWND)wParam, MAKEPOINTS(lParam) );
		break;
	case WM_HOTKEY:
		result = done = EvHotKey((int)wParam);
		break;
	case WM_LBUTTONUP:
	case WM_RBUTTONUP:
	case WM_NCLBUTTONUP:
	case WM_NCRBUTTONUP:
	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
	case WM_NCLBUTTONDOWN:
	case WM_NCRBUTTONDOWN:
	case WM_LBUTTONDBLCLK:
	case WM_RBUTTONDBLCLK:
	case WM_NCLBUTTONDBLCLK:
	case WM_NCRBUTTONDBLCLK:
		done = EventButton( uMsg, (int)wParam, MAKEPOINTS(lParam) );
		break;
	case WM_KEYUP:
	case WM_KEYDOWN:
		done = EventKey( uMsg, (int)wParam, (LONG)lParam );
		break;
	case WM_ACTIVATEAPP:
		done = EventActivateApp( (BOOL)wParam, (DWORD)lParam );
		break;
	case WM_ACTIVATE:
		EventActivate( LOWORD(wParam), HIWORD(wParam), (HWND)lParam );
		break;
	case WM_HSCROLL:
	case WM_VSCROLL:
		done = EventScroll( uMsg, LOWORD(wParam), HIWORD(wParam), (HWND)lParam );
		break;
	case WM_INITMENU:
	case WM_INITMENUPOPUP:
		done = EventInitMenu( uMsg, (HMENU)wParam, LOWORD(lParam), (BOOL)HIWORD(lParam) );
		break;
	case WM_MENUSELECT:
		done = EvMenuSelect( LOWORD(wParam), HIWORD(wParam), (HMENU)lParam );
		break;
	case WM_DROPFILES:
		done = EvDropFiles( (HDROP)wParam );
		break;
	case WM_CTLCOLORBTN:
	case WM_CTLCOLORDLG:
	case WM_CTLCOLOREDIT:
	case WM_CTLCOLORLISTBOX:
	case WM_CTLCOLORMSGBOX:
	case WM_CTLCOLORSCROLLBAR:
	case WM_CTLCOLORSTATIC:
		done = EventCtlColor( uMsg, (HDC)wParam, (HWND)lParam, (HBRUSH *)&result );
		break;
	case WM_KILLFOCUS:
	case WM_SETFOCUS:
		done = EventFocus( uMsg, (HWND)wParam );
		break;
	default:
		if ( uMsg >= WM_USER && uMsg < 0x7FFF || uMsg >= 0xC000 && uMsg <= 0xFFFF )
			result = done = EventUser( uMsg, wParam, lParam );
		else
			result = done = EventSystem( uMsg, wParam, lParam );
		break;
	}
	return	done ? result : DefWindowProc( uMsg, wParam, lParam );
}

LRESULT TWin::DefWindowProc( UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	return ::DefWindowProc( _hWnd, uMsg, wParam, lParam );
}

BOOL TWin::PreProcMsg( MSG* msg )
{
	if( _hAccel )
		return ::TranslateAccelerator( _hWnd, _hAccel, msg );
	return FALSE;
}

BOOL TWin::Sleep( UINT mSec )
{
	if ( mSec == 0 || _sleepBusy )
		return	TRUE;

	if ( ::SetTimer( _hWnd, TLIB_SLEEPTIMER, mSec, 0 ) == FALSE)
		return	FALSE;
	_sleepBusy = TRUE;

	MSG		msg;
	while( ::GetMessage(&msg, 0, 0, 0) )
	{
		if( msg.hwnd == _hWnd && msg.wParam == TLIB_SLEEPTIMER )
		{
			::KillTimer( _hWnd, TLIB_SLEEPTIMER );
			break;
		}
		if ( TApp::GetApp()->PreProcMsg( &msg ) )
			continue;

		::TranslateMessage( &msg );
		::DispatchMessage( &msg );
	}
	_sleepBusy = FALSE;

	return	TRUE;
}

BOOL TWin::SetForceForegroundWindow(void)
{
#ifndef SPI_GETFOREGROUNDLOCKTIMEOUT
#define SPI_GETFOREGROUNDLOCKTIMEOUT 0x2000
#define SPI_SETFOREGROUNDLOCKTIMEOUT 0x2001
#endif
	DWORD	foreId, targId, svTmOut;

	foreId = ::GetWindowThreadProcessId(::GetForegroundWindow(), NULL);
	targId = ::GetWindowThreadProcessId( _hWnd, NULL);
	if (foreId != targId)
		::AttachThreadInput(targId, foreId, TRUE);
	::SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, (void *)&svTmOut, 0);
	::SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, 0, 0);
	BOOL	ret = ::SetForegroundWindow( _hWnd );
	::SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, (void *)svTmOut, 0);
	if (foreId != targId)
		::AttachThreadInput(targId, foreId, FALSE);

	return	ret;
}

BOOL TWin::EvCommand(WORD wNotifyCode, WORD wID, LPARAM hwndCtl)
{
	return	FALSE;
}

BOOL TWin::EvSysCommand(WPARAM uCmdType, POINTS pos)
{
	return	FALSE;
}

BOOL TWin::EvCreate(LPARAM lParam)
{
	return	FALSE;
}

BOOL TWin::EvClose(void)
{
	return	FALSE;
}


BOOL TWin::EvMeasureItem(UINT ctlID, MEASUREITEMSTRUCT *lpMis)
{
	return	FALSE;
}

BOOL TWin::EvDrawItem(UINT ctlID, DRAWITEMSTRUCT *lpDis)
{
	return	FALSE;
}

BOOL TWin::EvNcDestroy(void)
{
	return	FALSE;
}

BOOL TWin::EvTimer(WPARAM timerID, TIMERPROC proc)
{
	return	FALSE;
}

BOOL TWin::EvQueryEndSession(BOOL nSession, BOOL nLogOut)
{
	return	TRUE;
}

BOOL TWin::EvEndSession(BOOL nSession, BOOL nLogOut)
{
	return	TRUE;
}

BOOL TWin::EvQueryOpen(void)
{
	return	TRUE;
}

BOOL TWin::EvPaint(void)
{
	return	FALSE;
}

BOOL TWin::EvNcPaint(HRGN hRgn)
{
	return	FALSE;
}

BOOL TWin::EvSize(UINT fwSizeType, WORD nWidth, WORD nHeight)
{
	return	FALSE;
}

BOOL TWin::EvShowWindow(BOOL fShow, int fnStatus)
{
	return	FALSE;
}

BOOL TWin::EvGetMinMaxInfo(MINMAXINFO *info)
{
	return	FALSE;
}

BOOL TWin::EvSetCursor(HWND cursorWnd, WORD nHitTest, WORD wMouseMsg)
{
	return	FALSE;
}

BOOL TWin::EvMouseMove(UINT fwKeys, POINTS pos)
{
	return	FALSE;
}

BOOL TWin::EvNcHitTest(POINTS pos, LRESULT *result)
{
	return	FALSE;
}

BOOL TWin::EvNotify(UINT ctlID, NMHDR *pNmHdr)
{
	return	FALSE;
}

BOOL TWin::EvContextMenu(HWND childWnd, POINTS pos)
{
	return	FALSE;
}

BOOL TWin::EvHotKey(int hotKey)
{
	return	FALSE;
}

BOOL TWin::EventActivateApp(BOOL fActivate, DWORD dwThreadID)
{
	return	FALSE;
}

BOOL TWin::EventActivate(BOOL fActivate, DWORD fMinimized, HWND hActiveWnd)
{
	return	FALSE;
}

BOOL TWin::EventScroll(UINT uMsg, int nCode, int nPos, HWND scrollBar)
{
	return	FALSE;
}

BOOL TWin::EventButton(UINT uMsg, int nHitTest, POINTS pos)
{
	return	FALSE;
}

BOOL TWin::EventKey(UINT uMsg, int nVirtKey, LONG lKeyData)
{
	return	FALSE;
}

BOOL TWin::EventInitMenu(UINT uMsg, HMENU hMenu, UINT uPos, BOOL fSystemMenu)
{
	return	FALSE;
}

BOOL TWin::EvMenuSelect(UINT uItem, UINT fuFlag, HMENU hMenu)
{
	return	FALSE;
}

BOOL TWin::EvDropFiles(HDROP hDrop)
{
	return	FALSE;
}

BOOL TWin::EventCtlColor(UINT uMsg, HDC hDcCtl, HWND hWndCtl, HBRUSH *result)
{
	return	FALSE;
}

BOOL TWin::EventFocus(UINT uMsg, HWND hFocusWnd)
{
	return	FALSE;
}

BOOL TWin::EventUser(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	return	FALSE;
}

BOOL TWin::EventSystem(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	return	FALSE;
}

UINT TWin::GetDlgItemText(int ctlId, LPSTR buf, int len)
{
	return	::GetDlgItemText(_hWnd, ctlId, buf, len);
}

UINT TWin::GetDlgItemTextV(int ctlId, void *buf, int len)
{
	return	TAPI32V::GetDlgItemTextV(_hWnd, ctlId, buf, len);
}

BOOL TWin::SetDlgItemText(int ctlId, LPCSTR buf)
{
	return	::SetDlgItemText(_hWnd, ctlId, buf);
}

BOOL TWin::SetDlgItemTextV(int ctlId, const void *buf)
{
	return	TAPI32V::SetDlgItemTextV(_hWnd, ctlId, buf);
}

int TWin::GetDlgItemInt(int ctlId, BOOL *err, BOOL sign)
{
	return	(int)::GetDlgItemInt(_hWnd, ctlId, err, sign);
}

BOOL TWin::SetDlgItemInt(int ctlId, int val, BOOL sign)
{
	return	::SetDlgItemInt(_hWnd, ctlId, val, sign);
}

HWND TWin::GetDlgItem(int ctlId)
{
	return	::GetDlgItem(_hWnd, ctlId);
}

BOOL TWin::CheckDlgButton(int ctlId, UINT check)
{
	return	::CheckDlgButton(_hWnd, ctlId, check);
}

UINT TWin::IsDlgButtonChecked(int ctlId)
{
	return	::IsDlgButtonChecked(_hWnd, ctlId);
}

BOOL TWin::IsWindowVisible(void)
{
	return	::IsWindowVisible(_hWnd);
}

BOOL TWin::EnableWindow(BOOL is_enable)
{
	return	::EnableWindow(_hWnd, is_enable);
}

int TWin::MessageBox(LPCSTR msg, LPCSTR title, UINT style)
{
	return	::MessageBox(_hWnd, msg, title, style);
}

int TWin::MessageBoxV(void *msg, void *title, UINT style)
{
	return	TAPI32V::MessageBoxV(_hWnd, msg, title, style);
}

BOOL TWin::BringWindowToTop(void)
{
	return	::BringWindowToTop(_hWnd);
}

BOOL TWin::PostMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	return	::PostMessage(_hWnd, uMsg, wParam, lParam);
}

BOOL TWin::PostMessageW(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	return	::PostMessageW(_hWnd, uMsg, wParam, lParam);
}

BOOL TWin::PostMessageV(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	return	TAPI32V::PostMessageV(_hWnd, uMsg, wParam, lParam);
}

LRESULT TWin::SendMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	return	::SendMessage(_hWnd, uMsg, wParam, lParam);
}

LRESULT TWin::SendMessageW(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	return	::SendMessageW(_hWnd, uMsg, wParam, lParam);
}

LRESULT TWin::SendMessageV(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	return	TAPI32V::SendMessageV(_hWnd, uMsg, wParam, lParam);
}

LRESULT TWin::SendDlgItemMessage(int idCtl, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	return	::SendDlgItemMessage(_hWnd, idCtl, uMsg, wParam, lParam);
}

LRESULT TWin::SendDlgItemMessageW(int idCtl, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	return	::SendDlgItemMessageW(_hWnd, idCtl, uMsg, wParam, lParam);
}

LRESULT TWin::SendDlgItemMessageV(int idCtl, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	return	TAPI32V::SendDlgItemMessageV(_hWnd, idCtl, uMsg, wParam, lParam);
}

BOOL TWin::GetWindowRect(RECT *rect)
{
	return	::GetWindowRect(_hWnd, rect ? rect : &_rect);
}

BOOL TWin::SetWindowPos(HWND hInsAfter, int x, int y, int cx, int cy, UINT fuFlags)
{
	return	::SetWindowPos(_hWnd, hInsAfter, x, y, cx, cy, fuFlags);
}

BOOL TWin::ShowWindow(int mode)
{
	return	::ShowWindow(_hWnd, mode);
}

BOOL TWin::SetForegroundWindow(void)
{
	return	::SetForegroundWindow(_hWnd);
}

HWND TWin::SetActiveWindow(void)
{
	return	::SetActiveWindow(_hWnd);
}

int TWin::GetWindowText(LPSTR text, int size)
{
	return	::GetWindowText(_hWnd, text, size);
}

int TWin::GetWindowTextV(void *text, int size)
{
	return	TAPI32V::GetWindowTextV(_hWnd, text, size);
}

BOOL TWin::SetWindowTextV(const void *text)
{
	return	TAPI32V::SetWindowTextV(_hWnd, text);
}

int TWin::GetWindowTextLengthV(void)
{
	return	TAPI32V::GetWindowTextLengthV(_hWnd);
}

BOOL TWin::SetWindowText(LPCSTR text)
{
	return	::SetWindowText(_hWnd, text);
}

LONG_PTR TWin::SetWindowLong(int index, LONG_PTR val)
{
	return	::SetWindowLong(_hWnd, index, val);
}

WORD TWin::SetWindowWord(int index, WORD val)
{
	return	::SetWindowWord(_hWnd, index, val);
}

LONG_PTR TWin::GetWindowLong(int index)
{
	return	::GetWindowLong(_hWnd, index);
}

WORD TWin::GetWindowWord(int index)
{
	return	::GetWindowWord(_hWnd, index);
}

BOOL TWin::MoveWindow(int x, int y, int cx, int cy, int bRepaint)
{
	return	::MoveWindow(_hWnd, x, y, cx, cy, bRepaint);
}

BOOL TWin::Idle(void)
{
	MSG		msg;

	if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		if (TApp::GetApp()->PreProcMsg(&msg))
			return	TRUE;

		::TranslateMessage(&msg);
		::DispatchMessage(&msg);
		return	TRUE;
	}

	return	FALSE;
}

UINT TWin::GetDlgItemTextU8(int ctlId, char *buf, int len)
{
	Wstr	wbuf(len);

	*buf = 0;
	GetDlgItemTextW(_hWnd, ctlId, wbuf.Buf(), len);

	return	WtoU8(wbuf, buf, len);
}

BOOL TWin::SetDlgItemTextU8(int ctlId, const char *buf)
{
	Wstr	wbuf(buf);

	return	::SetDlgItemTextW(_hWnd, ctlId, wbuf);
}

int TWin::MessageBoxU8(char *msg, char *title, UINT style)
{
	Wstr	wmsg(msg);
	Wstr	wtitle(title);

	return	::MessageBoxW(_hWnd, wmsg, wtitle, style);
}

int TWin::GetWindowTextU8(char *text, int len)
{
	Wstr	wbuf(len);

	wbuf.Buf()[0] = 0;
	if (::GetWindowTextW(_hWnd, wbuf.Buf(), len) < 0) return -1;

	return	WtoU8(wbuf, text, len);
}

BOOL TWin::SetWindowTextU8(const char *text)
{
	Wstr	wbuf(text);

	return	::SetWindowTextW(_hWnd, wbuf);
}

int TWin::GetWindowTextLengthU8(void)
{
	int		len = ::GetWindowTextLengthW(_hWnd);
	Wstr	wbuf(len + 1);

	if (::GetWindowTextW(_hWnd, wbuf.Buf(), len + 1) <= 0) return 0;

	return	WtoU8(wbuf, NULL, 0);
}

//////////////////////////////////////////////////////////////////////////
TSubClass::TSubClass( TWin* parent /* = NULL */ )
: TWin( parent)
, _oldProc( NULL )
{

}

TSubClass::~TSubClass()
{
	if( _oldProc && _hWnd )
		DetachWnd();
}

BOOL TSubClass::AttachWnd( HWND hWnd )
{
	TApp::GetApp()->AddWinByWnd( this, hWnd );
	_oldProc = (WNDPROC)::SetWindowLong( _hWnd, GWL_WNDPROC, (LONG_PTR)TApp::WinProc );
	return _oldProc ? TRUE : FALSE;
}

BOOL TSubClass::DetachWnd()
{
	if( !_oldProc || _hWnd )
		return FALSE;
	::SetWindowLong( _hWnd, GWL_WNDPROC, (LONG_PTR)_oldProc );
	_oldProc = NULL;
	return TRUE;
}

LRESULT	TSubClass::DefWindowProc( UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	return ::CallWindowProc( _oldProc, _hWnd, uMsg, wParam, lParam );
}

//////////////////////////////////////////////////////////////////////////
TSubClassCtl::TSubClassCtl( TWin* parent )
: TSubClass( parent )
{

}

BOOL TSubClassCtl::PreProcMsg( MSG *msg )
{
	if( _parent ) 
		return _parent->PreProcMsg( msg );
	return FALSE;
}
